package com.ddf.springdata.jpa.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.ddf.springdata.jpa.dao.impl.JapBaseDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;

/**
 * @author DDf on 2018/9/24
 * 参考 https://docs.spring.io/spring-data/jpa/docs/2.0.10.RELEASE/reference/html/#jpa.java-config
 * @Configuration 表明这是一个配置类
 * @ComponentScan("com.ddf.springdata.jpa") 指定spring的扫描组件包路径
 * @PropertySource 引入外部配置文件
 * @EnableTransactionManagement 开启基于注解的事务支持
 * @EnableJpaAuditing 开启Audit功能，能够对标注实体类的@CreateDate, @CreateTime, @LastModifyBy, @LastModifyTime的注解提供支持
 * @EnableAspectJAutoProxy 开启注解版的AOP功能
 * @ImportResource(value = "classpath:aop.xml") 引入配置文件，配置内容的作用参考一下链接第一个问题，但是没有看出来什么区别
 *      https://docs.spring.io/spring-data/jpa/docs/2.1.0.RELEASE/reference/html/#_common
 *
 *  *
 *  * 可以自定义Repository的基类，自定义一些通用方法，然后让Repository去继承它以便获得额外的扩展能力，基类必须
 *  * 在配置类上通过@EnableJpaRepositories(repositoryBaseClass = BaseJapDaoImpl.class)这种方式配置，否则里面的方法属性
 *  * 依然会按照对象属性去解析而报错；
 *  * @EnableJpaRepositories(basePackages = "com.ddf.springdata.jpa.dao", repositoryBaseClass = BaseJapDaoImpl.class)
 */
@Configuration
@ComponentScan("com.ddf.springdata.jpa")
@PropertySource("classpath:application.properties")
@EnableJpaRepositories(basePackages = "com.ddf.springdata.jpa.dao", repositoryBaseClass = JapBaseDaoImpl.class)
@EnableTransactionManagement
@EnableJpaAuditing
@EnableAspectJAutoProxy
@ImportResource(value = "classpath:aop.xml")
public class MainConfiguration {
    @Autowired
    private Environment environment;

    /**
     * 配置数据源
     *
     * @return
     */
    @Bean
    public DataSource druidDataSource() {
        System.out.println("-------------加载DataSource--------------");
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setName(environment.getProperty("jdbc.name"));
        dataSource.setUsername(environment.getProperty("jdbc.userName"));
        dataSource.setPassword(environment.getProperty("jdbc.password"));
        dataSource.setUrl(environment.getProperty("jdbc.url"));
        dataSource.setDriverClassName(environment.getProperty("jdbc.driverClassName"));
        return dataSource;
    }


    /**
     * 参考 https://docs.spring.io/spring-data/jpa/docs/2.0.10.RELEASE/reference/html/#jpa.java-config
     *
     * @param dataSource
     * @return
     */
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
        System.out.println("-------------加载LocalContainerEntityManagerFactoryBean--------------");
        LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean =
                new LocalContainerEntityManagerFactoryBean();
        localContainerEntityManagerFactoryBean.setDataSource(dataSource);
        // 配置 JPA 提供商的适配器
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        // vendorAdapter.setGenerateDdl(true);
        localContainerEntityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
        // <!-- 配置实体类所在的包 -->
        localContainerEntityManagerFactoryBean.setPackagesToScan("com.ddf.springdata.jpa.entity");
        // 配置 JPA 的基本属性. 例如 JPA 实现产品的属性
        Properties properties = new Properties();
        properties.setProperty("hibernate.show_sql", "true");
        properties.setProperty("hibernate.format_sql", "true");
        properties.setProperty("hibernate.hbm2ddl.auto", "update");
        localContainerEntityManagerFactoryBean.setJpaProperties(properties);
        return localContainerEntityManagerFactoryBean;
    }


    /**
     * 配置JPA的事务管理
     * 参考 https://docs.spring.io/spring-data/jpa/docs/2.0.10.RELEASE/reference/html/#jpa.java-config
     * @param entityManagerFactory
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        System.out.println("-------------加载PlatformTransactionManager--------------");
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }

    /**
     * 注册AuditorAware接口来实现对实体类@CreateBy @LastModityBy等注解的支持
     * @return
     */
    @Bean
    public AuditorAware<String> auditorProvider() {
        return new MyAuditAware();
    }
}
